#include <string.h>
#include "ZComDef.h"
#include "OSAL.h"
//#include "hal_uart.h"
#include "OSAL_Memory.h"
#include "OnBoard.h"
#include "ZGlobals.h"
#include "osal_Nv.h"
#include "bdb_interface.h"

#include "zcl.h"
#include "zcl_ota.h"
#include "ZDObject.h"

#include "mfg.h"
#include "cmd.h"
#include "cmd_uart.h"
#include "cmd_ota.h"
#include "ota_common.h"

//[NodeID][EP][cmd][seq][param]
typedef struct
{
  uint16 nodeId;
  uint8  ep;
  uint8  cmd;
}cmdOta_CnfParam_t;

uint8 OTA_SeqNo;

/*

*/
static void cmdOta_SendCnf(uint8 status, uint8 endpoint, uint8 transID, void* cnfParam);//zcl_SetSendExtParam(cmdZcl_SendCmdCnf,pCmdHdr,option);

static uint8 cmdOta_ImageNotify( afAddrType_t dstAddr, uint8 paramLen, uint8* paramData );

void cmdOTA_CmdOutput( uint16 nodeId, uint8 ep, uint8 cmd, uint8 paramLen, uint8* paramData );

void cmdOTA_CmdSendCnf( uint16 nodeId, uint8 ep, uint8 cmd );

void cmdOta_CmdInput(uint8 cmdSeq, uint8 cmdClass, uint8 cmdLen, uint8* cmdData)
{
  afAddrType_t dstAddr;
  uint8 cmdId;
  uint8 status = ZSuccess;
  cmdId = cmdData[0];
  dstAddr.addrMode = afAddr16Bit;
  dstAddr.addr.shortAddr = BUILD_UINT16( cmdData[1], cmdData[2] );
  dstAddr.endPoint = cmdData[3];
  uint8 paramLen = cmdLen - 5;
  uint8 *paramData = cmdData + 5;
  if( cmdId == OTA_CMD_NOTIFY )
  {
    cmdOta_ImageNotify( dstAddr, paramLen, paramData );
    cmd_UartAckSend(cmdSeq,cmdClass,1,&status);
  }
  else
  {
    OTA_Msg_t *pMsg;
    if (zclOTA_GetTaskID() != 0xff)
    {
      pMsg = (OTA_Msg_t*) osal_msg_allocate(paramLen + sizeof(OTA_Msg_t));
      if (pMsg)
      {
        pMsg->hdr.event = MT_SYS_OTA_MSG;
        pMsg->cmd = cmdId;
        pMsg->len = paramLen;
        osal_memcpy(pMsg->data, paramData, paramLen);        
        osal_msg_send(zclOTA_GetTaskID(), (uint8*) pMsg);
      }
    }
    //zclOTA_ServerHandleFileSysCb ( dstAddr, cmdId, paramLen, paramData );
  }
  
}

//[NodeID][EP][cmd][seq][param]
void cmdOTA_CmdOutput( uint16 nodeId, uint8 ep, uint8 cmd, uint8 paramLen, uint8* paramData )
{
  uint8 buf[64];
  buf[0] = cmd_UartGetSeqNum();
  buf[1] = CMD_CLASS_OTA_OUTPUT;
  buf[2] = cmd;
  buf[3] = LO_UINT16( nodeId );
  buf[4] = HI_UINT16( nodeId );
  buf[5] = ep;
  buf[6] = 0;
  memcpy( buf+7, paramData, paramLen );
  cmd_UartTxSend( paramLen+7, buf );
}

void cmdOTA_CmdSendCnf( uint16 nodeId, uint8 ep, uint8 cmd )
{
  uint8 buf[7];
  buf[0] = cmd_UartGetSeqNum();
  buf[1] = CMD_CLASS_OTA_INPUT;
  buf[2] = LO_UINT16( nodeId );
  buf[3] = HI_UINT16( nodeId );
  buf[4] = ep;
  buf[5] = cmd;
  buf[6] = 0;
  cmd_UartTxSend( 7, buf );
}

uint8 cmd_OtaFileReadReq( afAddrType_t *pAddr, zclOTA_FileID_t *pFileId,
                            uint8 size, uint32 offset)
{
  if( zclOTA_SeqNumEntryFree(pAddr->addr.shortAddr) )
  {
    uint8 buf[8 + 4 + 1];
    uint8 *p;
    uint8 len;
    p = OTA_FileIdToStream( pFileId, buf );
    *p++ = BREAK_UINT32(offset, 0);
    *p++ = BREAK_UINT32(offset, 1);
    *p++ = BREAK_UINT32(offset, 2);
    *p++ = BREAK_UINT32(offset, 3);
    *p = size;
    len = p - buf;
    cmdOTA_CmdOutput( pAddr->addr.shortAddr, pAddr->endPoint, OTA_CMD_FILE_READ, len, buf );
    return ZSuccess;
  }
  return ZFailure;
}

uint8 cmd_OtaGetImage( afAddrType_t *pAddr, zclOTA_FileID_t *pFileId,
                         uint16 hwVer, uint8 *ieee, uint8 options )
{
  if( zclOTA_SeqNumEntryFree(pAddr->addr.shortAddr) )
  {
    uint8 buf[8 + 1 + 2 + 8];
    uint8 len = 0;
    uint8 *p;
    p = OTA_FileIdToStream( pFileId, buf );
    *p++ = options;
    if( options & MT_OTA_HW_VER_PRESENT_OPTION )
    {
      *p++ = LO_UINT16(hwVer);
      *p++ = HI_UINT16(hwVer);
    }
    len = p - buf;
    if (ieee)
    {
      len += 8;
      osal_memcpy(p, ieee, Z_EXTADDR_LEN);
    }
    cmdOTA_CmdOutput( pAddr->addr.shortAddr, pAddr->endPoint, OTA_CMD_NEXT_IMG, len, buf );
    return ZSuccess;
  }
  return ZFailure;
}

uint8 cmd_OtaSendStatus(afAddrType_t *pAddr, uint8 type, uint8 status, uint8 optional, zclOTA_FileID_t *pFileId)
{
  uint8 buf[11];
  buf[0] = type;
  buf[1] = status;
  buf[2] = optional;
  OTA_FileIdToStream( pFileId, buf+3 );
  cmdOTA_CmdOutput( pAddr->addr.shortAddr, pAddr->endPoint, OTA_CMD_STATUS, sizeof(buf), buf );
  return ZSuccess;
}


//-----------------------------------------------------------------------------

static void cmdOta_SendCnf(uint8 status, uint8 endpoint, uint8 transID, void* cnfParam)
{
  if( cnfParam )
  {
    cmdOta_CnfParam_t *param = cnfParam;
    cmdOTA_CmdSendCnf( param->nodeId, param->ep, param->cmd );
    MFG_free( cnfParam );
  }
}

static uint8 cmdOta_ImageNotify( afAddrType_t dstAddr, uint8 param_len, uint8* paramData )
{
  uint8 status = ZFailure;
  zclOTA_ImageNotifyParams_t param;
  if( param_len < 10 )
  {
    return ZFailure;
  }
  param.payloadType = paramData[0];
  param.queryJitter = paramData[1];
  OTA_StreamToFileId( &param.fileId, paramData+2 );
  
  cmdOta_CnfParam_t *cnfParam = MFG_malloc(sizeof(cmdOta_CnfParam_t));
  if( cnfParam )
  {
    cnfParam->nodeId = dstAddr.addr.shortAddr;
    cnfParam->ep = dstAddr.endPoint;
    cnfParam->cmd = OTA_CMD_NOTIFY;
    zcl_SetSendExtParam(cmdOta_SendCnf,cnfParam,0);
    status = zclOTA_SendImageNotify( &dstAddr, OTA_SeqNo++, &param );
    if( status != ZSuccess )
    {
      MFG_free( cnfParam );
    }
  }
  return status;
}
